package hello.app.model;


import hello.app.model.data.Comment;
import hello.app.model.data.HelloContact;
import hello.app.model.data.User;
import hello.app.util.Util;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.*;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class HelloApi {
	private static HelloApi instance = null;
	public static final String BASE_URL = "http://twimaker.com/app/webroot/hello/request.php";
	private String mBaseUrl = "http://twimaker.com/app/webroot/hello/request.php";
	private String mDeviceId;

	public class HelloApiException extends IOException {

		public static final int NOT_FOUND_USER = 100;
		public static final int NOT_FOUND_CONTACT = 200;
		private static final long serialVersionUID = 1L;

		private int code = 0;
		public HelloApiException(String s, int code){
			super(s);
			this.code = code;
		}

		public int getCode(){
			return code;
		}

	}

	static HelloApi getInstance(String baseUrl){
		if(instance == null){
			instance = new HelloApi(baseUrl);
		}
		return instance;
	}

	static HelloApi getInstance(){
		if(instance == null){
			instance = new HelloApi(BASE_URL);
		}
		return instance;
	}

	private HelloApi(String baseUrl){
		this.mBaseUrl = baseUrl;
	}

	public void setSecurityDeviceId(String device_id){
		this.mDeviceId = device_id;
	}

	private String httpRequest(String url) throws URISyntaxException, ClientProtocolException, IOException {
		String json = null;
		HttpResponse response = null;
		DefaultHttpClient client = new DefaultHttpClient();
		URI uri = new URI(url);
		HttpUriRequest request = new HttpPost(uri);
		response = client.execute(request);
		if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
			HttpEntity httpEntity = response.getEntity();
			json = EntityUtils.toString(httpEntity);
		}
		client.getConnectionManager().shutdown();
		return json;
	}

	public void asynchronousRequest(String url, ResponseHandler<String> handler) throws URISyntaxException, ClientProtocolException, IOException{
		String response = null;
		DefaultHttpClient client = new DefaultHttpClient();
		URI uri = new URI(url);
		HttpUriRequest request = new HttpPost(uri);
		response = client.execute(request, handler);
	}

	/**
	 * ユーザーの追加
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 * @throws HelloApiException
	 */
	public User addUser() throws ClientProtocolException, URISyntaxException, IOException, JSONException, HelloApiException{
		String query = "?api=addUser&device_id=" + mDeviceId;
		String json = httpRequest(mBaseUrl + query);
		User user = null;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error")){
			user = getUserByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return user;
	}

	/**
	 * ユーザーをスーパーユーザーにする
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 * @throws HelloApiException
	 */
	public User changeSuperUser() throws ClientProtocolException, URISyntaxException, IOException, JSONException, HelloApiException{
		String query = "?api=changeSuperUser&device_id=" + mDeviceId;
		String json = httpRequest(mBaseUrl + query);
		User user = null;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error")){
			user = getUserByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return user;
	}

	/**
	 * ユーザー情報を取得
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 * @throws HelloApiException
	 */
	public User getUser() throws ClientProtocolException, URISyntaxException, IOException, JSONException, HelloApiException{
		String query = "?api=getUser&device_id=" + mDeviceId;
		String json = httpRequest(mBaseUrl + query);
		User user;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error")){
			user = getUserByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return user;
	}

	/**
	 * コンタクトのダメだねをプラスする
	 * @param contact_id
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 * @throws HelloApiException
	 */
	public HelloContact plusBadCount(String contact_id) throws ClientProtocolException, URISyntaxException, IOException, JSONException, HelloApiException{
		String query = "?api=plusBadCount&device_id=" + mDeviceId + "&contact_id=" + contact_id;
		String json = httpRequest(mBaseUrl + query);
		HelloContact contact = null;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error") && jsonObject.has("id")){
			contact = getContactByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return contact;
	}

	/**
	 * コンタクトのいいねをプラスする
	 * @param contact_id
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 * @throws HelloApiException
	 */
	public HelloContact plusGoodCount(String contact_id) throws ClientProtocolException, URISyntaxException, IOException, JSONException, HelloApiException{
		String query = "?api=plusGoodCount&device_id=" + mDeviceId + "&contact_id=" + contact_id;
		String json = httpRequest(mBaseUrl + query);
		HelloContact contact = null;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error") && jsonObject.has("id")){
			contact = getContactByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return contact;
	}

	/**
	 * コンタクトの評価を変化させる
	 * @param contact_id
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 * @throws HelloApiException
	 */
	public HelloContact changeCount(String contact_id) throws ClientProtocolException, URISyntaxException, IOException, JSONException, HelloApiException{
		String query = "?api=changeCount&device_id=" + mDeviceId + "&contact_id=" + contact_id;
		String json = httpRequest(mBaseUrl + query);
		HelloContact contact = null;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error") && jsonObject.has("id")){
			contact = getContactByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return contact;
	}

	public HelloContact addContact(String number) throws ClientProtocolException, URISyntaxException, IOException, JSONException, HelloApiException{
		String query = "?api=addContact&number=" + number;
		String json = httpRequest(mBaseUrl + query);
		HelloContact contact = null;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error") && jsonObject.has("id")){
			contact = getContactByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return contact;
	}

	/**
	 * コンタクトを取得する
	 * @param number
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 * @throws HelloApiException
	 */
	public HelloContact getContact(String number) throws ClientProtocolException, URISyntaxException, IOException, JSONException, HelloApiException{
		String query = "?api=getContact&number=" + number + "&device_id=" + mDeviceId;
		String json = httpRequest(mBaseUrl + query);
		HelloContact contact = null;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error") && jsonObject.has("id")){
			contact = getContactByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return contact;
	}

	/**
	 * コンタクトをまとめて取得する
	 * @param numbers
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws HelloApiException
	 * @throws JSONException
	 */
	public HelloContact[] getContacts(String[] numbers) throws ClientProtocolException, URISyntaxException, IOException, HelloApiException, JSONException{
		String[] temp = new String[numbers.length];
		for(int i = 0; i < numbers.length; i++){
			temp[i] = "number["+ String.valueOf(i) +"]=" + numbers[i];
		}
		String query = "?api=getContacts&" + Util.join(temp, "&") + "&device_id=" + mDeviceId;
		String json = httpRequest(mBaseUrl + query);
		HelloContact[] contacts = null;
		try{
			JSONArray jsonArray = new JSONArray(json);
			contacts = new HelloContact[jsonArray.length()];
			for(int i = 0; i < jsonArray.length(); i++){
				contacts[i] = getContactByJSON(jsonArray.getJSONObject(i));
			}
		}catch(JSONException e){
			JSONObject jsonObject = new JSONObject(json);
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return contacts;
	}

	/**
	 * 自分のコメントを取得する
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 */
	public Comment[] getMyComments() throws ClientProtocolException, URISyntaxException, IOException, JSONException{
		String query = "?api=getMyComments&device_id=" + mDeviceId;
		String json = httpRequest(mBaseUrl + query);
		Comment[] comments = null;
		try{
			JSONArray jsonArray = new JSONArray(json);
			comments = new Comment[jsonArray.length()];
			for(int i = 0; i < jsonArray.length(); i++){
				comments[i] = getCommentByJSON(jsonArray.getJSONObject(i));
			}
		}catch(JSONException e){
			JSONObject jsonObject = new JSONObject(json);
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return comments;
	}

	/**
	 * 複数のコンタクトのコメントを取得する
	 * @param contact_id
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 */
	public HashMap<String ,Comment[]> getComments(String[] contactIds) throws ClientProtocolException, URISyntaxException, IOException, JSONException{
		String[] temp = new String[contactIds.length];
		for(int i = 0; i < contactIds.length; i++){
			temp[i] = "contact_id["+ String.valueOf(i) +"]=" + contactIds[i];
		}
		String query = "?api=getComments&" + Util.join(temp, "&");;
		String json = httpRequest(mBaseUrl + query);
		HashMap<String ,Comment[]> map = new HashMap<String, Comment[]>();
		try{
			JSONObject jsonObject = new JSONObject(json);
			@SuppressWarnings("unchecked")
			Iterator<String> iter = jsonObject.keys();
			while(iter.hasNext()){
				String key = iter.next();
				JSONArray jsonArray = jsonObject.getJSONArray(key);
				Comment[] comments = new Comment[jsonArray.length()];
				for(int i = 0; i < jsonArray.length(); i++){
					comments[i] = getCommentByJSON(jsonArray.getJSONObject(i));
				}
				map.put(key, comments);
			}
		}catch(JSONException e){
			JSONObject jsonObject = new JSONObject(json);
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return map;
	}

	public Comment[] getComments(int contact_id) throws ClientProtocolException, URISyntaxException, IOException, JSONException{
		return getComments(Integer.toString(contact_id));
	}

	/**
	 * コンタクトのコメントを取得する
	 * @param contact_id
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 */
	public Comment[] getComments(String contact_id) throws ClientProtocolException, URISyntaxException, IOException, JSONException{
		String query = "?api=getComments&contact_id=" + contact_id;
		String json = httpRequest(mBaseUrl + query);
		Comment[] comments = null;
		try{
			JSONArray jsonArray = new JSONArray(json);
			comments = new Comment[jsonArray.length()];
			for(int i = 0; i < jsonArray.length(); i++){
				comments[i] = getCommentByJSON(jsonArray.getJSONObject(i));
			}
		}catch(JSONException e){
			JSONObject jsonObject = new JSONObject(json);
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return comments;
	}

	public Comment saveComment(int contact_id, String text) throws ClientProtocolException, URISyntaxException, IOException, JSONException{
		return saveComment(Integer.toString(contact_id), text);
	}

	/**
	 * コメントをセーブする
	 * @param contact_id
	 * @param text
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws JSONException
	 */
	public Comment saveComment(String contact_id, String text) throws ClientProtocolException, URISyntaxException, IOException, JSONException{
		String query = String.format("?api=saveComment&device_id=%s&contact_id=%s&text=%s", mDeviceId, contact_id, URLEncoder.encode(text,"UTF-8"));
		String json = httpRequest(mBaseUrl + query);
		Comment comment = null;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error") && jsonObject.has("id")){
			comment = getCommentByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return comment;
	}

	public Comment addGoodCommentCount(int comment_id) throws JSONException, ClientProtocolException, URISyntaxException, IOException{
		return addGoodCommentCount(Integer.toString(comment_id));
	}

	public Comment addGoodCommentCount(String comment_id) throws JSONException, ClientProtocolException, URISyntaxException, IOException{
		String query = String.format("?api=addGoodCommentCount&device_id=%s&comment_id=%s", mDeviceId, comment_id);
		String json = httpRequest(mBaseUrl + query);
		Comment comment = null;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error") && jsonObject.has("id")){
			comment = getCommentByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return comment;
	}

	public Comment addBadCommentCount(int comment_id) throws JSONException, ClientProtocolException, URISyntaxException, IOException{
		return addBadCommentCount(Integer.toString(comment_id));
	}

	public Comment addBadCommentCount(String comment_id) throws JSONException, ClientProtocolException, URISyntaxException, IOException{
		String query = String.format("?api=addBadCommentCount&device_id=%s&comment_id=%s", mDeviceId, comment_id);
		String json = httpRequest(mBaseUrl + query);
		Comment comment = null;
		JSONObject jsonObject = new JSONObject(json);
		if(!jsonObject.has("error") && jsonObject.has("id")){
			comment = getCommentByJSON(jsonObject);
		}else{
			JSONObject error = jsonObject.getJSONObject("error");
			throw new HelloApiException(error.getString("msg"), error.getInt("code"));
		}
		return comment;
	}

	private User getUserByJSON(JSONObject jsonObject) throws JSONException{
		boolean super_user = jsonObject.getInt("super") == 1 ? true : false;
		String device_id = jsonObject.getString("device_id");
		int id = jsonObject.getInt("id");
		return new User(id, device_id, super_user);
	}

	private Comment getCommentByJSON(JSONObject jsonObject) throws JSONException{

		return new Comment(
				jsonObject.getLong("id"),
				jsonObject.getInt("contact_id"),
				jsonObject.getInt("user_id"),
				null,
				jsonObject.getString("text"),
				jsonObject.getInt("good"),
				jsonObject.getInt("bad"),
				jsonObject.getString("created_at"),
				jsonObject.getString("updated_at")
				);
	}

	private HelloContact getContactByJSON(JSONObject jsonObject) throws JSONException{
		String name = jsonObject.getString("name");
		String phone_number = jsonObject.getString("number");
		String address = jsonObject.getString("address");
		int good = jsonObject.getInt("good");
		int bad = jsonObject.getInt("bad");
		int id = jsonObject.getInt("id");
		String typeName;
		try{
			typeName = jsonObject.getString("type_name");
			if(typeName == "null"){
				typeName = "";
			}
		}catch(Exception e){
			typeName = "";
		}
		int evalState;
		try{
			evalState = jsonObject.getInt("eval_state");
			switch(evalState){
			case 0:
				evalState = HelloContact.BAD_EVALUATATION;
				break;
			case 1:
				evalState = HelloContact.GOOD_EVALUATATION;
				break;
			case -1:
			default:
				evalState = HelloContact.NON_EVALUATATION;
			break;
			}
		}catch(Exception e){
			evalState = HelloContact.NON_EVALUATATION;
		}
		HelloContact contact = new HelloContact(id, name, phone_number, good, bad, evalState, typeName, address);
		JSONArray jsonArray = jsonObject.getJSONArray("comments");
		Comment[] comments = new Comment[jsonArray.length()];
		for(int i = 0; i < jsonArray.length(); i++){
			comments[i] = getCommentByJSON(jsonArray.getJSONObject(i));
		}
		contact.setComments(comments);
		return contact;
	}


}
